package pers.lx.crm.base.common.plugin;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.springframework.jdbc.core.ParameterMapper;
import pers.lx.crm.base.constant.KeyConstants;
import pers.lx.crm.base.util.ReflectUtil;
import pers.lx.crm.base.util.StringUtil;
import pers.lx.crm.entiy.BaseDO;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

/**
 * Created by lxl on 2017/2/14.
 */
@Intercepts( @Signature( type = StatementHandler.class, method = "prepare",args = {Connection.class}))
public class PagePlugin implements Interceptor {

    //所需拦截ID 正则匹配
    private String pageSqlId = ".*ListPage.*";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取handler对象
        RoutingStatementHandler handler = (RoutingStatementHandler)invocation.getTarget() ;
        //通过反射获取delegate属性
        StatementHandler delegate = (StatementHandler)ReflectUtil.getFieldValueByFieldName(handler, KeyConstants.DELEGATE) ;
        //通过反射获取到delegate父类BaseStatementHandler的mappedStatement属性
        MappedStatement mappedStatement = (MappedStatement)ReflectUtil.getFieldValueByFieldName(delegate,KeyConstants.MAPPEDSTATEMENT) ;
        //获取到boundsql对象
        BoundSql boundSql = delegate.getBoundSql() ;
        Object obj = boundSql.getParameterObject() ;
        //判断对象是否能够支持分页
        if(obj instanceof BaseDO && mappedStatement.getId().matches(pageSqlId)){
            BaseDO page = (BaseDO)obj ;
            //拦截到的prepare方法的参数是一个Connection对象
            Connection connection = (Connection)invocation.getArgs()[0] ;
            //构造sql查询总数
            this.setTotal(page, mappedStatement, connection);
            //当前拦截的sql语句
            String sql = boundSql.getSql() ;
            String pageSql = StringUtil.getPageSql(page,sql) ;
            //利用反射注入sql语句
            ReflectUtil.setFieldValueByFieldName(boundSql,KeyConstants.SQL,pageSql);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    private void setTotal(BaseDO page,MappedStatement statement,Connection connection){
        //获取到BoundSql
        BoundSql boundSql = statement.getBoundSql(page) ;
        //获取sql语句并构造查询sql语句
        String sql = boundSql.getSql() ;
        String countSql = StringUtil.getCountSql(sql) ;
        //获取到参数映射
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings() ;
        //利用构造好的查询sql语句创建新的boundsql对象
        BoundSql countBoundSql = new BoundSql(statement.getConfiguration(),countSql,parameterMappings,page) ;
        //通过获得的参数创建一个新的ParameterHandler对象
        ParameterHandler handler = new DefaultParameterHandler(statement,page,countBoundSql) ;
        //通过connection建立数据库连接
        PreparedStatement preparedStatement = null ;
        ResultSet resultSet = null ;
        try {
            preparedStatement = connection.prepareStatement(countSql) ;
            //通过handler给statement设置参数
            handler.setParameters(preparedStatement) ;
            resultSet = preparedStatement.executeQuery() ;

            if( resultSet.next()){
                page.setTotal(resultSet.getInt(1));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet!=null){
                    resultSet.close();
                }
                if (preparedStatement!=null){
                    preparedStatement.close();
                }
            } catch (SQLException e){
                e.printStackTrace();
            }
        }

    }

}
